package simulator;

import java.util.LinkedList;
import java.util.Scanner;

/**
 * 
 * Esta classe implementa o comportamento de um receptor..<br><br>
 * Possuí métodos para:<br>
 * - Iniciar a recepçao de dados {@link Sender.Data}.<br>
 * - Terminar a recepçao de dados {@link Sender.Data}.<br>
 *
 * @author Grupo de Redes do ISCTE (20012/13)
 *
 */

public class Receiver {
	public enum State { WAIT, RX };
	private State state;
	private boolean erros;
	private int RxIndex = 0;
	private SlidingWindow window;
	
	public Receiver() {
		window = new SlidingWindow(TxRxSystem.W);
		state = State.WAIT;
	}
	
	/**
	 * Inicia a recepção de dados.
	 * 
	 * @param      Os dados recebidos.	
	*/
	public void startRX(Data data) {

		state = State.RX;
		
		// Update statistics		
		
		// Output
		String s = "[Receiver@";
		s=s+Simulator.getClock()+" Start RX Data ID: "+data.getID()+"]";
		Simulator.debug(s);	
		
        s=""+Simulator.getClock()+"\t"+"Srx"+"\t"+data.getID()+"\t";
        s=s+data.getTimeStamp()+"\t"+"-"+"\t"+"-"+"\t"+Simulator.getClock()+"\t"+"-"+"\t"+"-"+"\t"+"-";
		Simulator.data(s);		
	}
	
	public void stopRX(Data data) {
		LinkedList<Data> confirmed = new LinkedList<Data>();
		
		if(TxRxSystem.DEMO){
			Scanner scanner = new Scanner(System.in);
			System.out.println("Trama " + data.getID() + ", recebido com erros ? (s/n)");
			String rsp = scanner.next();
			if(rsp.equalsIgnoreCase("s")){
				erros = true;
			}else{
				erros = false;
			}
		}else{
			double pTramaComErros = 1 - (Math.pow(1-TxRxSystem.PB, data.getSize()));
			double random = Math.random();
			
			if(random <= pTramaComErros){
				erros = true;
				Simulator.info("erro na trama!");
			}else{
				erros = false;
			}
		}
		
		if(!erros && (data.getID() >= RxIndex) && (data.getID() < RxIndex + window.windowSize())){
			window.add(data);
			
			for (Data d : window) {
				if(d.getID() == RxIndex){
					confirmed.add(d);
					RxIndex ++;
				}
			}
			window.removeAll(confirmed);
			
			Data ack = new Data(Simulator.getClock()+0.0, RxIndex, 0);
			double tp = TxRxSystem.DISTANCIA/TxRxSystem.VP;
			TxRxEvent newEvent = new TxRxEvent (Simulator.getClock()+tp, TxRxEvent.TxRxEventType.Ack, ack);
			Simulator.addEvent(newEvent);
		}
		
		// Update statistics
		/* Ao tempo total no sistema, delaySys é adicionado o tempo passado no 
		 * sistema (tempoActual-tempoGeração) da trama que acabou de ser recebida
		 */
		TxRxSystem.delaySys+=Simulator.getClock()-data.getTimeStamp();
		
		// Output
		String s = "[Receiver@";
		s=s+Simulator.getClock()+" Stop RX Data ID (State="+state+"): "+data.getID()+"]";
		Simulator.debug(s);
		
        s=""+Simulator.getClock()+"\t"+"Erx"+"\t"+data.getID()+"\t";
        s=s+data.getTimeStamp()+"\t"+"-"+"\t"+"-"+"\t"+"-"+"\t"+Simulator.getClock()+"\t"+"-"+"\t"+"-";
		Simulator.data(s);		
	}
	
}
